﻿using System;
using System.Linq;

using System.Collections.Generic;

using Utilities;
using Annotations;
using System.Reflection;

namespace Integration
{
    public class CsvDeserializer<T> where T : class, new()
    {
        public CsvDeserializer()
        {
            Type type = typeof(T);

            // Cache the length of the fields
            _propertyLengths = new Dictionary<string, int>();

            foreach (PropertyInfo propertyInfo in type.GetProperties())
            {
                LengthAttribute lengthAttribute = propertyInfo.GetAttribute<LengthAttribute>(false);

                if (lengthAttribute != null)
                {
                    _propertyLengths.Add(propertyInfo.Name, lengthAttribute.Value);
                }
            }

            // Cache the property accessors
            _propertyAccessors = type.GetTypeAccessor().PropertyAccessors;
        }

        private IDictionary<string, PropertyAccessor> _propertyAccessors;

        private IDictionary<string, int> _propertyLengths;

        public IList<CsvDeserializerFieldMapping> Mapping { get; set; }

        public T Deserialize(IList<string> row)
        {
            PreCondition.RequireNotNull(Mapping, "Mapping");
            
            T instance = new T();

            foreach (CsvDeserializerFieldMapping mapping in Mapping)
            {
                string propertyName = mapping.PropertyName;
                PropertyAccessor propertyAccessor = _propertyAccessors[propertyName];
                object value = row[mapping.Index];

                if (propertyAccessor.PropertyType != typeof(string))
                {
                    value = Convert.ChangeType(value, propertyAccessor.PropertyType);
                }
                else // It is a string
                {
                    string v = (string)value;

                    // If the length of the string is specified and the length of the data is greater than the length
                    if (_propertyLengths.ContainsKey(propertyName))
                    {
                        int length = _propertyLengths[propertyName];

                        if (mapping.TruncateToFit)
                        {                            
                            value = v.Substring(0, length);
                        }
                        else
                        {
                            throw new InvalidOperationException("Cannot deserialize field: '" + propertyName
                                + "'. Length of data (" + v.Length + ") is greater than capacity (" + length + ")");
                        }
                    }
                }

                propertyAccessor.SetValue(instance, value);
            }

            return instance;
        }
    }
}
